package com.briercliffe.settlers.core.domain;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * User: briercliffe
 * Date: 10/15/13
 */
public class BoardLayout
{
    // ==================================================
    // class static data
    // ==================================================

    // ==================================================
    // class static methods
    // ==================================================
    public static BoardLayout fromLayout(final Layout iLayout)
    {
        BoardLayout boardLayout = new BoardLayout();
        String[][] layout = iLayout.getLayout();
        // Layout is in Odd-r Coordinates
        for (int i = 0; i < layout.length; i++)
        {
            String[] row = layout[i];
            for (int j = 0; j < row.length; j++)
            {
                // Odd-r coords are given by (col, row) which is why the order
                // below is (j, i)
                CubeCoordinates cubeCoordinates = CubeCoordinates.fromOddROffset(j, i);
                AxialCoordinates coords = AxialCoordinates.fromCubeCoordinates(cubeCoordinates);
                Tile tile = Tile.createFromTileType(TileType.getFromKey(row[j]));
                tile.setAxialCoordinates(coords);
                boardLayout.storeTile(tile, coords);
            }
        }

        return boardLayout;
    }

    // ==================================================
    // instance data
    // ==================================================
    Map<Integer, Map<Integer, Tile>> mTiles;
    // ==================================================
    // factories
    // ==================================================

    // ==================================================
    // constructors
    // ==================================================
    public BoardLayout()
    {
        mTiles = new HashMap<>();
    }
    // ==================================================
    // public methods
    // ==================================================

    /**
     * Get a collection of all the tiles from the board.
     *
     * @return All the tiles.
     */
    public Collection<Tile> getAllTiles()
    {
        List<Tile> tiles = new ArrayList<>();
        for (Map.Entry<Integer, Map<Integer, Tile>> row : mTiles.entrySet())
        {
            for (Map.Entry<Integer, Tile> col : row.getValue().entrySet())
            {
                tiles.add(col.getValue());
            }
        }
        return tiles;
    }

    /**
     * Get the tile at the given axial coordinates.
     *
     * @param iCoordinates The coordinates of the tile.
     * @return The tile at the given coordinates or null if no tile.
     */
    public Tile getTile(final AxialCoordinates iCoordinates)
    {
        return getTileFromAxialCoords(iCoordinates);
    }

    /**
     * Get the tile at the given axial coordinates.
     *
     * @param iQ The Q coordinate.
     * @param iR The R coordinate.
     * @return The tile at the given coordinates or null if no tile.
     */
    public Tile getTile(final int iQ, final int iR)
    {
        return getTileFromAxialCoords(new AxialCoordinates(iQ, iR));
    }

    /**
     * Get the neighbors of a tile at the given axial coordinates values.
     * @param iQ - The Q value of the axial coordinates.
     * @param iR - The R value of the axial coordinates.
     * @return A collection of tiles that neighbor the tile at the given axial coordinates.
     */
    public Collection<Tile> getNeighbors(final int iQ, final int iR)
    {
        return getNeighborsFromAxialCoords(new AxialCoordinates(iQ, iR));
    }

    /**
     * Get the nieghbors of a tile at the given axial coordinates.
     * @param iCoordinates - The axial coordinates.
     * @return A collection of tiles that neighbor the tile at the given axial coordinates.
     */
    public Collection<Tile> getNeighbors(final AxialCoordinates iCoordinates)
    {
        return getNeighborsFromAxialCoords(iCoordinates);
    }

    /**
     * Store a tile at the given axial coordinates.
     *
     * @param iTile - The tile to store.
     * @param iCoordinates - The coordinates to place the tile.
     */
    public void storeTile(final Tile iTile, final AxialCoordinates iCoordinates)
    {
        Map<Integer, Tile> row = mTiles.get(iCoordinates.getQ());
        if (row == null)
        {
            mTiles.put(iCoordinates.getQ(), new HashMap<Integer, Tile>());
            row = mTiles.get(iCoordinates.getQ());
        }
        row.put(iCoordinates.getR(), iTile);
    }
    // ==================================================
    // non public methods
    // ==================================================

    private Collection<Tile> getNeighborsFromAxialCoords(final AxialCoordinates iCoordinates)
    {
        Collection<Tile> tiles = new ArrayList<>();
        for(Direction direction : Direction.values())
        {
            AxialCoordinates neighbor = iCoordinates.getNeighbor(direction);
            getTile(neighbor);
        }
        return tiles;
    }

    private Tile getTileFromAxialCoords(final AxialCoordinates iCoordinates)
    {
        Map<Integer, Tile> row = mTiles.get(iCoordinates.getQ());
        if (row != null)
        {
            return row.get(iCoordinates.getR());
        }
        return null;
    }
    // ==================================================
    // getters / setters
    // ==================================================

    public Map<Integer, Map<Integer, Tile>> getTiles()
    {
        return mTiles;
    }

    public void setTiles(final Map<Integer, Map<Integer, Tile>> iTiles)
    {
        mTiles = iTiles;
    }

    // ==================================================
    // public inner classes
    // ==================================================
    public static enum Direction {
        NE(new Integer[] {1,-1}),
        E(new Integer[] {1,0}),
        SE(new Integer[] {0,1}),
        SW(new Integer[] {-1,1}),
        W(new Integer[] {-1,0}),
        NW(new Integer[] {0,-1});

        private Integer[] mNeighborDelta;

        private Direction(final Integer[] iNeighborDelta)
        {
            mNeighborDelta = iNeighborDelta;
        }

        public int getQ() {
            return mNeighborDelta[0];
        }

        public int getR() {
            return mNeighborDelta[1];
        }
    }

}
